﻿using Common;
using DriverMockEnvi;
using System;
using System.Net.Sockets;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AppEnvi;
using System.Net;

using System.Threading;

namespace AppEnvi
{
    [System.AddIn.AddIn("AppEnvi")]
    public class AppEnvi : ModuleBase
    {
        // TODO: The following parameters should be set interactively
        // via the WP7 GUI emulator.

        // Initialize the longTermAverage to a bogus value so that
        // we can check when averaging algorithm is first run. We'll
        // use this to determine when to initalize the variables in the
        // averaging algorithm.
        double longTermAverage = -100.0;

        // Default sensitivity value 0.8
        double sensitivity = 0.8;

        // Rudimentary way to set either email or SMS notifications.
        string rn = RoleNotifications.OpEmailName;
        string rn1 = RoleNotifications.OpRxEmail;
     
        public override void Start()
        {
                
            try
            {
                // Try to set the averaging sensitivity via a command-line argument
                string[] words = moduleInfo.Args();
                sensitivity = double.Parse(words[0]);
            }
            catch (Exception e)
            {
                logger.Log("No or incorrect sensitivity given. Using {0} as default",
                    sensitivity.ToString());
            }

            // Subscribe to the Envi Driver to obtain timeseries input for the
            // averaging algorithm
            IList<View.VPort> allPortsList = GetAllPortsFromPlatform();
            foreach (View.VPort port in allPortsList)
            {
                if (Role.ContainsRole(port, RoleSensor.RoleName) && port.GetInfo().ModuleFacingName() == "envi")
                {
                    View.VPort enviPort = port;
                    View.VCapability enviPortCapability = GetCapability(port, Globals.UserSystem);

                    port.Subscribe(RoleSensor.RoleName, RoleSensor.OpGetName, this.ControlPort,
                        enviPortCapability, this.ControlPortCapability);
                }
            }

                        
        }

        // No shutdown mechanism required
        public override void Stop() { }

        // If a new port is registered, check to see if it is an Envi Driver
        // If it is an Envi Driver, subscribe to it.
        public override void PortRegistered(View.VPort port)
        {
            if (Role.ContainsRole(port, RoleSensor.RoleName) && port.GetInfo().ModuleFacingName() == "envi")
            {
                View.VPort enviPort = port;
                View.VCapability enviPortCapability = GetCapability(port, Globals.UserSystem);

                port.Subscribe(RoleSensor.RoleName, RoleSensor.OpGetName, this.ControlPort,
                    enviPortCapability, this.ControlPortCapability);
            }

        }

        public override void PortDeregistered(View.VPort port) { }




        //  Method to Check for a new email and take some appropriate action
        public void CheckEmail()
        {
            int i = 0; 
            string subject, sender, body,control="";
            IList<View.VPort> allPortsList = GetAllPortsFromPlatform();
             
             foreach (View.VPort port in allPortsList)
             {
                 if (Role.ContainsRole(port, RoleNotifications.RoleName) && port.GetInfo().ModuleFacingName() == "extendedNotifications")
                 {
                     View.VPort notificationPort = port;
                     View.VCapability notificationPortCapability = GetCapability(notificationPort,
                            Globals.UserSystem);
                     IList<View.VParamType> msgParams = new List<View.VParamType>();
                     IList<View.VParamType> temp
                                 = notificationPort.Invoke(RoleNotifications.RoleName, rn1, msgParams,
                                   this.ControlPort, notificationPortCapability, this.ControlPortCapability);

                    
                     while(i < (int)temp.Count)
                     {
                         #region Parsing received emails & taking action
                         // Parsing email to take switching off / on action on the Ismart Power Board

                         sender = (string)temp[i].Value(); i++;
                         subject = (string)temp[i].Value(); i++;
                         body = (string)temp[i].Value(); i++;

                         if (body.Contains("yes") || body.Contains("Yes")) { control = "a"; logger.Log("Switching off"); }
                         if (body.Contains("no") || body.Contains("No") ) { control = "A"; logger.Log("Switching on"); }

                         IPEndPoint ip = new IPEndPoint(IPAddress.Parse("10.66.110.181"), 9760);
                         Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                         try
                         {
                             server.Connect(ip);
                         }
                         catch (SocketException e)
                         {
                             Console.WriteLine("Unable to connect to server.");
                             return;
                         }
                         try
                         {
                         string a = server.Send(Encoding.ASCII.GetBytes(control)).ToString();
                         byte[] data = new byte[1];
                         //  server.Blocking = false;
                         // Using non blocking socket because we donot "need" the response from ismart device.
                         
                             int receivedDataLength = server.Receive(data, 1, SocketFlags.None);
                         }
                         catch (Exception e) { logger.Log("Exception in rx-tx"); }

                         //  string stringData = Encoding.ASCII.GetString(data, 0, receivedDataLength);
                         //  logger.Log("\n Data rx ::::::::::: {0}\n", stringData);
                         server.Shutdown(SocketShutdown.Both);
                         server.Close();
                         #endregion
                     }
                     break;  
                 }
                 
                 
             }
            
            
        }


        // If data is received from the Envi Driver, analyze the data using an
        // averaging algorithm. This simple algorithm computes a short term and
        // a long-term average. If the averages are sufficiently different (ie, if
        // recent energy levels have suddenly changed), notify the user.
        //
        // Notifications are made using the ``Notifications'' Driver provided by
        // HomeOS. Settings for the notifications, such as the email address, phone
        // number and SMTP server are provided as command-line arguments when
        // instantiating the Notifications driver.

        public override void AsyncReturn(string roleName, string opName,
            IList<View.VParamType> retVals, View.VPort senderPort)
        {

       
            // This is invoked when the Envi device gets new data (once every 6 seconds)            



            CheckEmail();  // UNCOMMENT  for receiving replies to email notifications and taking action

            // Instead of passing objects through the ParamType interface, it
            // would be more OO-ish to define an EnviDatum class to serve
            // as an interface/contract between the Envi App and Driver:
            //IList<EnviDatum> enviData = (List<EnviDatum>)retVals[0].Value();
            //EnviDatum e = enviData[0];
            //double current = e.getPower();
            //logger.Log("Timestamp Received: {0}, {1}", e.getTime().ToString(), 
            //    e.getPower().ToString());


            double current = Convert.ToDouble(retVals[0].Value());
            //double current = double.Parse(tpl[1].ToString());
            logger.Log("Energy value received: {0}", current.ToString());


            // If the averaging algorithm is being run for the first time,
            // initialize the longTermAverage to the current value.
            if (longTermAverage == -100.0)
                longTermAverage = current;

            double shortTermAverage = 0.9 * current + 0.1 * longTermAverage;
            longTermAverage = 0.1 * current + 0.9 * longTermAverage;


            // Print the state of the averaging algorithm for debugging:
            //logger.Log("> Sensitivity: {0}", sensitivity.ToString());
            //logger.Log("> Current Value: {0}", current.ToString());
            //logger.Log("> Short Term Average: {0}", shortTermAverage.ToString());
            //logger.Log("> Long Term Average: {0}", longTermAverage.ToString());
            //double lhs = Math.Abs(Math.Abs(longTermAverage) - Math.Abs(shortTermAverage));
            //logger.Log("> Difference: {0}", lhs.ToString());
            //double rhs = (1.0 - sensitivity) * Math.Abs(longTermAverage);
            //logger.Log("> Threshold: {0}", rhs.ToString());


            // If the long and short term averages differ significantly, notify the user.
            if (Math.Abs(Math.Abs(longTermAverage) - Math.Abs(shortTermAverage)) >
                (1.0 - sensitivity) * Math.Abs(longTermAverage))
            {
                logger.Log("Start sending email...");
                IList<View.VPort> allPortsList = GetAllPortsFromPlatform();

                foreach (View.VPort port in allPortsList)
                {
                    if (Role.ContainsRole(port, RoleNotifications.RoleName) && port.GetInfo().ModuleFacingName() == "extendedNotifications")
                    {
                        View.VPort notificationPort = port;
                        logger.Log("Got the notification port...");
                        string subject = "Notification from HomeOS";
                        string message = ("The Envi device associated with your HomeOS platform recently " +
                            "received a measurement of " + current.ToString() + " W." +
                            "\nThis is a significant change from previous recent energy consumption, " +
                            " which was approximately " + longTermAverage.ToString() + " W. Do you want to turn it off ?");

                        IList<View.VParamType> msgParams = new List<View.VParamType>();
                        msgParams.Add(new ParamType(ParamType.SimpleType.text, "", subject, "subject"));
                        msgParams.Add(new ParamType(ParamType.SimpleType.text, "", message, "message"));
                        View.VCapability notificationPortCapability = GetCapability(notificationPort,
                            Globals.UserSystem);

                        IList<View.VParamType> temp 
                         = notificationPort.Invoke(RoleNotifications.RoleName, rn, msgParams,
                           this.ControlPort, notificationPortCapability, this.ControlPortCapability);

                        logger.Log("Notified user via " + rn);
                      
                        break;
                    }
                }

            }

        }

    }

}
